//----BEGIN LICENSE----//
/*****************************************************************\
** prevalentLite - sqlite3 persistence object library 
**
** Copyright (C) 2008 Philip R. Schuchardt
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
**
*****************************************************************/
//----END LICENSE----//


/*!
  \class plPointer plPointer.h <plPointer>
  \brief A database aware pointer

        This class hold a pointer to an object.  This
        class makes transparent database loading for
        objects.  You should use a plPointer the same way that you use a normal c++ pointer.  Make sure you call plData::Setup(), so this data is aware of the database.

        Example:
        \code
            plPointer<MyClass> ptr;
            ptr = new MyClass();
            ptr->DoSomething();
            MyClass* realPtr = ptr.Data();

            MyClass* obj = new MyClass();
            ptr = obj;

            delete ptr;
        \endcode
*/

#ifndef plPOINTER_H
#define plPOINTER_H

//Our includes
#include "plData.h"
#include "plDatabase.h"

//Qt includess
//#include <QMetaType>

template <class T>
        class plPointer : public plData {

        public:
    //Constructor
    plPointer();

    //Should never be called by the user
    plPointer(T* value);

    //Copy constructor
    plPointer(const plPointer<T>& obj);

    //Modifier
    void SetId(sqlite_int64 id);

    /*!
          \brief Get's the real object pointer
          \return T* the real object pointer
        */
    T* Data() const;

    //Modifier
    void SetData(T* value);

    //Operator overloading
    void operator=(T* value);

    //Operator overloading
    void operator=(const plPointer<T>& obj);

    //Operator overloading
    T* operator->();

    //Convertion operator
    operator T*() const;

    //Binds the data with a statement
    virtual void Bind(sqlite3_stmt* statement, int index = 1) const;

    //Extracts the data from a column
    virtual void Column(sqlite3_stmt* statement, int index = 0);

    //The classes name
    static const char* GetClassName() { return "plPointer"; }

        private:
    T* Value; //The pointer to the data in memory
    sqlite_int64 Id; //The pointer to data on the database

    //Does a smart copy of the object
    void Copy(const plPointer<T>& obj);


}; //end plPointer



//Constructor
template <class T>	
        plPointer<T>::plPointer() : plData() {
    Value = NULL;
    Id = -1;
    //Cached = false;
}


//Copy constructor
template <class T>
        plPointer<T>::plPointer(const plPointer<T>& obj) : plData(obj) {
    Copy(obj);
}


//Should never be called by the user
template <class T>
        plPointer<T>::plPointer(T* value) {
    Value = value;
    if(Value) {
        Id = value->Id();
    } else {
        Id = 0; //NULL value
    }
}

//Accessor
template <class T>
        T* plPointer<T>::Data() const {

    //For new's in contructors
    if(Value && Id < 0) {
        //The user create a Value in readonly context but it's not
        //cached, So we need to delete it.
        plPointer<T>* thisObject = const_cast<plPointer<T>* >(this);

        delete thisObject->Value;
        thisObject->Value = NULL;

        //We have an invalid id
        plDatabase* database = GetDatabase();
        database->GetData(thisObject); //Gets the valid id

        //Try to get the pointer from the database
        database = GetDatabase();
        thisObject->Value = database->GetObject<T>(Id);
        //thisObject->Cached = true;
    } else {

        plPointer<T>* thisObject = const_cast<plPointer<T>* >(this);

        //If we have an invalid Id
        if( Id < 0) {
            //We have an invalid id
            plDatabase* database = GetDatabase();

            database->GetData(thisObject);
        } //end if(Value == NULL && id < 0)

        //If the id is set but the value isn't loaded
        if(Value == NULL && Id > 0) {
            //Try to get the pointer from the database
            plDatabase* database = GetDatabase();
            thisObject->Value = database->GetObject<T>(Id);
            //thisObject->Cached = true;
        } //end if (value == NULL && Id > 0)
    }

    return Value;
}

//Modifier
template <class T>
        void plPointer<T>::SetData(T* value) {  
    Value = value;
    if(Value) {
        Id = Value->Id();
    } else {
        if(GetDatabase()->IsReadOnly()) {
            Id = -1;
        } else {
            Id = 0; //NULL id because this is a null pointer
        }
    }
    plData::SetData();
}


//Operator overloading
template <class T>
        void plPointer<T>::operator=(T* value) {
    SetData(value);
}

//Convertion operator
template <class T>
        plPointer<T>::operator T*() const {
    return Data();
}



//Operator overloading
template <class T>
        void plPointer<T>::operator=(const plPointer<T>& obj) {
    Copy(obj);
}


//Operator overloading
template <class T>
        T* plPointer<T>::operator->() {
    return Data();
}


//Binds the data with the statement
template <class T>
        void plPointer<T>::Bind(sqlite3_stmt* statement, int index) const {
    sqlite3_bind_int64(statement, index, Id);
}

//Extracts the data from a column
template <class T>
        void plPointer<T>::Column(sqlite3_stmt* statement, int index) {
    Id = sqlite3_column_int64(statement, index);
}


//Smartly copies the objects
//Does a smart copy of the object
template <class T>
        void plPointer<T>::Copy(const plPointer<T>& obj) {
    SetData(obj.Value);
    if(obj.Id > 0) {
        Id = obj.Id;
    }
}


//typedef plPointer<plDataObject> plPointerVariant;
//Q_DECLARE_METATYPE(plPointerVariant)

#endif // plPOINTER_H


